﻿using System;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Web;
using System.Web.UI;
using System.Xml;
using DevDefined.OAuth.Consumer;
using DevDefined.OAuth.Framework;
using TheOutfield.UmbExt.UniversalMediaPicker.Controls;
using TheOutfield.UmbExt.UniversalMediaPicker.Interfaces;
using TheOutfield.UmbExt.UniversalMediaPicker.Types;

namespace TheOutfield.UmbExt.UniversalMediaPicker.Dialogs
{
    public partial class UniversalMediaPickerOauth : Page
    { 
        public string AccessToken;
        public string ClientIdCallBack;
        private const string UserAgent = "Mozilla/4.0 UMP API (compatible; MSIE 6.0; Windows NT 5.1)";
        private string _providerAlias;
        private string _requstToken;

        protected void Page_Load(object sender, EventArgs e)
        {
            //Get providerAlias from qrystring
            if(!string.IsNullOrEmpty(Request.QueryString["providerAlias"]))
            {
                _providerAlias = Request.QueryString["providerAlias"];
            }
            if (!string.IsNullOrEmpty(Request.QueryString["clientIdCallBack"]))
            {
                Session["UMP_ClientIdCallBack"] = Request.QueryString["clientIdCallBack"];
            }
            //Check if AccessToken is available and set it and return.
            if(!string.IsNullOrEmpty(Request.QueryString["AccessToken"]))
            {
                AccessToken = Request.QueryString["AccessToken"];
                ClientIdCallBack = Session["UMP_ClientIdCallBack"].ToString();
                return;
            }

            //Create provider from alias
            if(!string.IsNullOrEmpty(_providerAlias))
            {
                IProvider provider = CreateProviderFromAlias(_providerAlias);
                var config = provider.ConfigControl as AbstractOauthConfigControl;
                if (config != null && config.OauthConfig != null)
                {
                    //Check if Provider is using an external app for authentication
                    if(config.OauthConfig.UseExternalAuthCallback)
                    {
                        Response.Redirect(config.CallbackUrl);
                    }

                    Session["UMP_OAuthType"] = config.OauthConfig.OAuthType;
                    Session["UMP_OAuthConfig"] = config.OauthConfig;
                    Session["UMP_CallBackUrl"] = config.CallbackUrl;
                    Session["UMP_RequestTokenRedirected"] = false;
                    Session["UMP_AccessTokenRedirected"] = false;
                    Session["UMP_ProviderAlias"] = _providerAlias;
                }
            }

            OAuth.OAuthTypes oAuthTypes = (OAuth.OAuthTypes)Session["UMP_OAuthType"];
            IOauthConfig oAuthConfig = Session["UMP_OAuthConfig"] as IOauthConfig;
            if(oAuthTypes.Equals(OAuth.OAuthTypes.OAuth1))
            {
                var callbackUrl = Session["UMP_CallBackUrl"].ToString();
                OAuth1(oAuthConfig, callbackUrl);
            }
            else
            {
                OAuth2(oAuthConfig);
            }

            ClientIdCallBack = Session["UMP_ClientIdCallBack"].ToString();
        }

        private IProvider CreateProviderFromAlias(string alias)
        {
            var currentProvider = UniversalMediaPickerProviderFactory.CreateProvider(alias, "");
            return currentProvider;
        }

        private void OAuth1(IOauthConfig authConfig, string callBackUrl)
        {
            //Check RequestToken redirect status
            bool requestRedirected;
            if (bool.TryParse(Session["UMP_RequestTokenRedirected"].ToString(), out requestRedirected) && !requestRedirected)
            {
                OAuth1_GetRequest(authConfig, callBackUrl);
            }

            //Get RequestToken from qrystring by Name (code)
            if (requestRedirected && string.IsNullOrEmpty(_requstToken))
            {
                _requstToken = Request.QueryString[authConfig.RequestTokenName];
            }

            //Check AccessToken redirect status
            bool accessRedirected;
            if (bool.TryParse(Session["UMP_AccessTokenRedirected"].ToString(), out accessRedirected) && !accessRedirected && !string.IsNullOrEmpty(_requstToken))
            {
                //Get AccessToken from qrystring by Name (access_token)
                OAuth1_GetAccess(_requstToken);
            }
        }

        private void OAuth1_GetRequest(IOauthConfig authConfig, string callBackUrl)
        {
            var consumerContext = new OAuthConsumerContext
            {
                ConsumerKey = authConfig.ConsumerKey,
                ConsumerSecret = authConfig.ConsumerSecret,
                SignatureMethod = SignatureMethod.HmacSha1
            };

            IOAuthSession session = new OAuthSession(consumerContext, authConfig.RequestTokenUrl, authConfig.UserAuthorizeUrl, authConfig.AccessTokenUrl(string.Empty), callBackUrl)
                .WithQueryParameters(new { scope = authConfig.Scope })
                .RequiresCallbackConfirmation();

            // get a request token from the provider
            IToken requestToken = session.GetRequestToken();

            Session["UMP_OAuth1_OAuthSession"] = session;
            Session["UMP_OAuth1_RequestToken"] = requestToken;

            // generate a user authorize url for this token (which you can use in a redirect from the current site)
            string authorizationLink = session.GetUserAuthorizationUrlForToken(requestToken, callBackUrl);
            Session["UMP_RequestTokenRedirected"] = true;
            Response.Redirect(authorizationLink);
        }

        private void OAuth1_GetAccess(string verifier)
        {
            IOAuthSession session = Session["UMP_OAuth1_OAuthSession"] as IOAuthSession;
            IToken requestToken = Session["UMP_OAuth1_RequestToken"] as IToken;

            if(session == null || requestToken == null)
            {
                AccessToken = "Error occured";
                return;
            }

            Session["UMP_AccessTokenRedirected"] = true;
            IToken accessToken = session.ExchangeRequestTokenForAccessToken(requestToken, verifier);
            AccessToken = accessToken.Token;
        }

        private void OAuth2(IOauthConfig authConfig)
        {

            //Check RequestToken redirect status
            bool requestRedirected;
            if (bool.TryParse(Session["UMP_RequestTokenRedirected"].ToString(), out requestRedirected) && !requestRedirected)
            {
                Session["UMP_RequestTokenRedirected"] = true;
                Response.Redirect(authConfig.RequestTokenUrl);
            }

            //Get RequestToken from qrystring by Name (code)
            if (requestRedirected && string.IsNullOrEmpty(_requstToken))
            {
                _requstToken = Request.QueryString[authConfig.RequestTokenName];
            }

            //Check AccessToken redirect status
            bool accessRedirected;
            if (bool.TryParse(Session["UMP_AccessTokenRedirected"].ToString(), out accessRedirected) && !accessRedirected && !string.IsNullOrEmpty(_requstToken))
            {
                Session["UMP_AccessTokenRedirected"] = true;
                //Get AccessToken from request
                AccessToken = OAuth2_GetAccess(authConfig.AccessTokenName, authConfig.AccessTokenUrl(_requstToken));
            }

            if (!string.IsNullOrEmpty(Request.QueryString[authConfig.AccessTokenName]))
            {
                AccessToken = Request.QueryString[authConfig.AccessTokenName];
            }
        }

        private void OAuth2_GetRequest()
        {}

        private string OAuth2_GetAccess(string accessTokenName, string accessTokenUrl)
        {
            string token = string.Empty;
            string response = WebRequest(accessTokenUrl);

            if (response.Contains("<?xml version=\"1.0\" encoding=\"utf-8\" ?>"))
            {
                XmlDocument document = new XmlDocument();
                document.LoadXml(response);
                XmlNode node = document.SelectSingleNode("//token");
                token = node == null ? "EMPTY" : node.InnerText;
            }
            else if(response.Length > 0)
            {
                //Store the returned access token
                NameValueCollection qs = HttpUtility.ParseQueryString(response);

                if (qs[accessTokenName] != null)
                {
                    token = qs[accessTokenName];
                }
            }
            return token;
        }

        private string WebRequest(string url)
        {
            HttpWebRequest webRequest = null;
            string responseData = "";

            webRequest = System.Net.WebRequest.Create(url) as HttpWebRequest;
            webRequest.Method = "GET";
            webRequest.ServicePoint.Expect100Continue = false;
            webRequest.UserAgent = UserAgent;
            webRequest.Timeout = 20000;

            StreamReader responseReader = null;
            try
            {
                responseReader = new StreamReader(webRequest.GetResponse().GetResponseStream());
                responseData = responseReader.ReadToEnd();
            }
            catch
            {
                throw;
            }
            finally
            {
                webRequest.GetResponse().GetResponseStream().Close();
                responseReader.Close();
                responseReader = null;
            }

            webRequest = null;
            return responseData;
        }
    }
}